package some

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"path"
	"time"

	"gitee.com/thubcc/p2pgit/classedpack"
	"gitee.com/thubcc/p2pgit/constant"
	"gitee.com/thubcc/p2pgit/format"
	"gitee.com/thubcc/p2pgit/types"
	"github.com/gin-gonic/gin"
)

// NewPackCB call back
func NewPackCB(pps *types.SomeRawer) func(*format.Header, []byte) error {

	return func(h *format.Header, b []byte) (cerr error) {
		pf := new(bytes.Buffer)
		pf.Reset()
		buf := h.ToByte()

		if h.Header32.Size != uint32(len(b)+len(buf)) {
			return fmt.Errorf("Size error:%d,%d,%d", len(b), len(buf), h.Header32.Size)
		}
		h.Header32.HCRC32 = h.Header32.CRC()
		h32 := h.Header32.ToHash256()
		_, cerr = pf.Write(h32[:])
		if cerr != nil {
			return
		}

		_, cerr = pf.Write(buf)
		if cerr != nil {
			return
		}
		_, cerr = pf.Write(b)
		pfb := pf.Bytes()
		// var ps = *pps
		// for k, p := range ps {
		// 	cerr = p.Put(pfb)
		// 	if cerr == constant.ErrorNeedSplit {
		// 		var tobedel types.Rawer
		// 		tobedel, cerr = ps.SplitK(k)
		// 		if cerr == nil {
		// 			fmt.Println(tobedel.(*classedpack.File).Name(), "removed")
		// 		}
		// 	}
		// 	if cerr != nil {
		// 		return
		// 	}
		// }
		cerr = pps.Put(pfb)
		return
	}
}

// Geter2Rawer Con
func Geter2Rawer(gs types.SomeGeter) (rs []types.Rawer) {
	for _, v := range gs {
		switch r := v.(type) {
		case *classedpack.File:
			rs = append(rs, r)
			fmt.Println(r.Name())
		case *classedpack.Client:
			rs = append(rs, r)
		case types.SomeGeter:
			var vrs = types.NewRawer(Geter2Rawer(r))
			rs = append(rs, &vrs)
		}
	}
	return
}

// Handle http handle
func Handle(rs *types.SomeRawer, e *gin.Engine) (err error) {

	var getHandle = func(gc *gin.Context) {
		var err error
		hs := gc.Query("hash")
		var s types.Selector
		switch len(hs) {
		case 0:
			err = fmt.Errorf("miss hash")
		case 64:
			var h32 types.Hash256
			var buf []byte
			buf, err = hex.DecodeString(hs)
			if err == nil {
				copy(h32[:], buf)
				s = types.FullSelect(h32)
			}
		case 40:
			var hash types.Hash
			var buf []byte
			buf, err = hex.DecodeString(hs)
			if err == nil {
				copy(hash[:], buf)
				s = types.HashSelect(hash)
			}
		default:
			err = fmt.Errorf("invalid hash length")
		}
		if err != nil {
			gc.JSON(400, gin.H{"error": err.Error()})
			return
		}
		var raw []byte
		raw, err = rs.GetRaw(s)
		if err != nil {
			gc.JSON(400, gin.H{"error": err.Error(), "select": s.String()})
			return
		}
		r := bytes.NewReader(raw)
		var h *format.Header
		h, err = format.ExtFromFile(r)
		if err != nil {
			gc.JSON(400, gin.H{"error": err.Error()})
			return
		}
		var hh = h.ToMap()
		gc.DataFromReader(200, int64(h.Header32.Size)+32-int64(h.ByteLen()), "application/octet-stream", r, hh)
		return
	}
	var putHandle = func(pc *gin.Context) {
		var err error
		cl := int(pc.Request.ContentLength)
		var raw = make([]byte, cl)
		_, err = io.ReadFull(pc.Request.Body, raw)
		if err != nil {
			pc.JSON(400, gin.H{"error": err.Error()})
			return
		}
		pc.Request.Body.Close()
		err = format.TravelRaw(raw, func(h32 *types.Header32, r []byte) (err error) {
			if !rs.Hit(h32.Hash) {
				err = fmt.Errorf("hit not matched %s", rs.Name())
				return
			}
			return rs.Put(r)
		})
		if err != nil {
			pc.JSON(400, gin.H{"error": err.Error()})
			return
		} else {
			pc.JSON(200, gin.H{"error": "nil"})
			return
		}
	}
	err = rs.Open(true)
	if err != nil {
		fmt.Println(err)
	}
	e.GET("/get", getHandle)
	e.POST("/put", putHandle)
	var apis []string
	for _, h := range rs.Some() {
		if c, ok := h.(*classedpack.File); ok {
			apis = append(apis, path.Base(c.Name()))
		}
	}
	e.GET("/api", func(c *gin.Context) {
		c.JSON(200, apis)
	})
	go func() {
		for {
			<-time.After(constant.SyncTimer)
			rs.Sync()
		}
	}()
	return
}
